﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.Runtime.InteropServices;
using System.Threading;
using DirectShowLib;

namespace Dare.Utilities.Media
{
    public enum MediaPlayerState {
        NoFile,
        OpeningFile,
        Ready,
        Stopped,
        Paused,
        Buffering,
        Playing,
        Initializing,
        Exiting
    }

    public enum MediaPlayerTextScrollDirection {
        UpAndLeftToRight = 31,
        UpAndRightToLeft = 30,
        DownAndLeftToRight = 11,
        DownAndRightToLeft = 10,
        LeftAndUpToDown = 21,
        LeftAndDownToUp = 20,
        RightAndUpToDown = 41,
        RightAndDownToUp = 40,
        CenterAndLeftToRight = 01,
        CenterAndRightToLeft = 00
    }

    public enum MediaPlayerVideoScaleMode {
        Original,
        HalfScale,
        DoubleScale,
        Stretch,
        Crop,
        MaintainAspect
    }

    public class MediaPlayer : IDisposable, ISampleGrabberCB {
        private int frameCount = 0;

        private const int WMGraphNotify = 0x0400 + 13;
        private const int VolumeFull = 0;
        private const int VolumeSilence = -10000;
        private const int S_OK = 0x00000000; //成功 
        private const int S_FALSE = 0x00000001; //失败 

        private IFilterGraph2 filterGraph = null;
        private IMediaControl mediaControl = null;
        private IMediaEventEx mediaEventEx = null;
        private IVideoWindow videoWindow = null;
        private IBasicAudio basicAudio = null;
        private IBasicVideo basicVideo = null;
        private IMediaSeeking mediaSeeking = null;
        private IMediaPosition mediaPosition = null;
        private IVideoFrameStep frameStep = null;

        private bool _isFullScreen = false;
        private int _currentVolume = VolumeFull;
        private MediaPlayerState _currentState = MediaPlayerState.NoFile;
        private MediaPlayerVideoScaleMode _scaleMode = MediaPlayerVideoScaleMode.MaintainAspect;
        private double _currentPlaybackRate = 1.0;

        private bool _showVideoWindow = true;

        public bool ShowVideoWindow {
            get { return _showVideoWindow; }
            set { _showVideoWindow = value; }
        }

        private int hr;
        private IntPtr owner = IntPtr.Zero;
        private IntPtr parentOwner = IntPtr.Zero;
        private Rectangle ownerRegion = Rectangle.Empty;
        private Image currentVideoImage = null;
        private Bitmap bitmapOverlay = null;
        private Font timeFont = null;

        private bool _saftyMode = true;


        private System.Threading.Thread threadEvent = null;
        private System.Threading.ManualResetEvent manualResetEvent = null;

        private System.Threading.ManualResetEvent manualResetEventForCaptureImage = new ManualResetEvent(false);

        //Video Info
        private bool _hasVideo = false;
        private bool _hasAudio = false;
        private string _fileName = string.Empty;
        private bool _canSeek;
        private bool _canStep;
        private long _videoDuration;
        private long _currentPos;
        private int _videoWidth = 0;
        private int _videoHeight = 0;
        private int _videoStride = 0;

        //Text Info
        private bool _textEnabled = false;
        private string _textString = String.Empty;
        private string _showString = String.Empty;
        private int _textScrollTimes = -1;
        private int _textScrollSpeed = 1;
        private Color _textBGColor = Color.Black;
        private Color _textColor = Color.White;
        private string _textFontName = String.Empty;
        private Font _textFont = null;
        private SizeF _textSize;
        private Bitmap _textAreaBitmap = null;
        private Graphics _textAreaGraph = null;
        private MediaPlayerTextScrollDirection _textDirection = MediaPlayerTextScrollDirection.DownAndRightToLeft;
        private Point _textAreaPosition = new Point(0, 0);
        private PointF _textPosition = new PointF(0, 0);
        private StringFormat _textStringFormat = StringFormat.GenericTypographic;

        public delegate void DoOnPlayCompleted();
        public delegate void DoOnError(string msg);
        public delegate void DoOnStateChanged(MediaPlayerState state);

        public event DoOnPlayCompleted PlayCompleted;
        public event DoOnStateChanged StateChanged;
        public event DoOnError Error;

        //更新显示文字
        private void UpdateShowString()
        {
            if (_textDirection == MediaPlayerTextScrollDirection.DownAndLeftToRight
                || _textDirection == MediaPlayerTextScrollDirection.UpAndLeftToRight
                || _textDirection == MediaPlayerTextScrollDirection.LeftAndUpToDown
                || _textDirection == MediaPlayerTextScrollDirection.RightAndUpToDown
                || _textDirection == MediaPlayerTextScrollDirection.CenterAndLeftToRight)
            {
                char[] charArray = _textString.ToCharArray();
                Array.Reverse(charArray);
                _showString = new string(charArray);
            }
            else
            {
                _showString = _textString;
            }
        }

        //启用字幕
        public bool TextEnabled {
            get { return _textEnabled; }
            set { _textEnabled = value; }
        }
        //字幕文字
        public string TextString {
            get { return _textString; }
            set {
                _textString = value;
                UpdateShowString();
                SetupBitmap();
            }
        }
        //分辨率
        public string VideoFormat {
            get { return _videoWidth.ToString() + "*" + _videoHeight.ToString(); }
        }
        //字幕滚动方向
        public MediaPlayerTextScrollDirection TextDirection {
            get { return _textDirection; }
            set {
                _textDirection = value;
                UpdateShowString();
                SetupBitmap();
            }
        }

        //当前播放器状态
        public MediaPlayerState CurrentState {
            get {
                return _currentState;
            }
        }

        //视频窗口是否可见（一般给内部播放器窗口调用）
        public bool VideoWindowVisible {
            get {
                OABool val;
                videoWindow.get_Visible(out val);
                return val == OABool.True;
            }
            set {
                try
                {
                    if (_hasVideo && videoWindow != null)
                    {
                        if (value)
                        {
                            videoWindow.put_Visible(OABool.True);
                        }
                        else
                        {
                            videoWindow.put_Visible(OABool.False);
                        }
                    }
                }
                catch { }
            }
        }

        public MediaPlayerVideoScaleMode ScaleMode {
            get {
                return _scaleMode;
            }
            set {
                _scaleMode = value;
                if (_currentState == MediaPlayerState.Playing ||
                    _currentState == MediaPlayerState.Buffering
                    ) {
                    SetupScaleMode();
                }
            }
        }

        public bool SaftyMode {
            get {
                return _saftyMode;
            }
            set {
                _saftyMode = value;
            }
        }

        public MediaPlayer() {
            filterGraph = new FilterGraph() as IFilterGraph2;
            mediaControl = filterGraph as IMediaControl;
            videoWindow = filterGraph as IVideoWindow;
            mediaSeeking = filterGraph as IMediaSeeking;

            // Get the event handle the graph will use to signal
            // when events occur
            IntPtr hEvent;
            mediaEventEx = filterGraph as IMediaEventEx;
            hr = mediaEventEx.GetEventHandle(out hEvent);
            DsError.ThrowExceptionForHR(hr);

            // Wrap the graph event with a ManualResetEvent
            manualResetEvent = new ManualResetEvent(false);
            manualResetEvent.SafeWaitHandle = new global::Microsoft.Win32.SafeHandles.SafeWaitHandle(hEvent, true);

            threadEvent = new System.Threading.Thread(new System.Threading.ThreadStart(this.EventWait));
            threadEvent.IsBackground = true;
            threadEvent.Start();
        }

        // Wait for events to happen.  This approach uses waiting on an event handle.
        // The nice thing about doing it this way is that you aren't in the windows message
        // loop, and don't have to worry about re-entrency or taking too long.  Plus, being
        // in a class as we are, we don't have access to the message loop.
        // Alternately, you can receive your events as windows messages.  See
        // IMediaEventEx.SetNotifyWindow.
        private void EventWait() {
            // Returned when GetEvent is called but there are no events
            const int E_ABORT = unchecked((int)0x80004004);

            int hr;
            IntPtr p1, p2;
            EventCode ec;

            do {
                // Wait for an event
                manualResetEvent.WaitOne(-1, true);

                // Avoid contention for _currentState
                lock (this) {
                    // If we are not shutting down
                    if (_currentState != MediaPlayerState.Exiting) {
                        // Read the event
                        for (
                            hr = mediaEventEx.GetEvent(out ec, out p1, out p2, 0);
                            hr >= 0;
                            hr = mediaEventEx.GetEvent(out ec, out p1, out p2, 0)
                            ) {
                            // Write the event name to the debug window
                            //Debug.WriteLine(ec.ToString());

                            // If the clip is finished playing       

                            switch (ec) {
                                case EventCode.Complete:
                                    // Call Stop() to set state
                                    this.Stop();

                                    // Throw event
                                    if (PlayCompleted != null) {
                                        PlayCompleted();
                                    }
                                    break;
                                case EventCode.BufferingData:
                                    _currentState = MediaPlayerState.Buffering;
                                    break;
                            }

                            // Release any resources the message allocated
                            hr = mediaEventEx.FreeEventParams(ec, p1, p2);
                            DsError.ThrowExceptionForHR(hr);
                        }

                        // If the error that exited the loop wasn't due to running out of events
                        if (hr != E_ABORT) {
                            DsError.ThrowExceptionForHR(hr);
                        }
                    } else {
                        // We are shutting down
                        break;
                    }
                }
            } while (true);
        }

        public void Open() {
            Open(null, false, false);
        }

        public void Open(string fileName) {
            Open(fileName, false, false);
        }

        public void Open(bool autoStart) {
            Open(null, autoStart, false);
        }

        public void Open(string fileName, bool autoStart, bool useAsynMethod) {
            if (useAsynMethod) {
                _fileName = fileName;

                System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(_OpenAsyn), autoStart);
            } else {
                _Open(fileName, autoStart);
            }
        }

        private void _OpenAsyn(object obj) {
            _Open(null, (bool)obj);
        }

        private void _Open(string fileName, bool autoStart) {
            if (!(fileName == null || fileName == String.Empty)) {
                _fileName = fileName;
            }

            if (_fileName == null || _fileName == String.Empty) {
                ChangeCurrentState(MediaPlayerState.NoFile);
                ProcessOnError("没有指定文件!", new System.IO.FileNotFoundException("没有指定文件"));
            } else {
                ChangeCurrentState(MediaPlayerState.OpeningFile);
                try {
                    InitFilterGraph();
                    InitVideoWindow();
                    InitMediaInfo();
                    ChangeCurrentState(MediaPlayerState.Ready);
                    if (autoStart) {
                        Play();
                    }
                } catch (Exception ex) {
                    ChangeCurrentState(MediaPlayerState.NoFile);
                    ProcessOnError("对不起，本播放器无法播放该文件!", ex);
                }
            }
        }

        public void Close() {
            if (_currentState == MediaPlayerState.Playing
                || _currentState == MediaPlayerState.Stopped
                || _currentState == MediaPlayerState.Paused
                || _currentState == MediaPlayerState.Ready
                || _currentState == MediaPlayerState.Buffering) {
                if (_currentState == MediaPlayerState.Playing
                    || _currentState == MediaPlayerState.Buffering) {
                    mediaControl.Stop();
                }
                RemoveAllFilters();
                _fileName = String.Empty;
                ChangeCurrentState(MediaPlayerState.NoFile);
            }
        }

        //改变当前状态，并触发OnStateChange事件
        private void ChangeCurrentState(MediaPlayerState state) {
            if (_currentState != state) {
                _currentState = state;
                if (StateChanged != null) {
                    StateChanged(state);
                }
            }
        }

        //当发生错误时内部处理
        private void ProcessOnError(string msg, Exception ex) {
            if (Error != null) {
                Error(msg);
            }
        }

        #region 初始化组件

        private void CustomRenderFileGraph(string _fileName) {
            string ext = String.Empty;
            int dotPos = _fileName.LastIndexOf('.');
            if (dotPos > -1) {
                ext = _fileName.Substring(dotPos).ToLower();
            }

            switch (ext) {
                case ".wmv":
                case ".asf":
                    CustomRenderWMVGraph(_fileName);
                    break;
                case ".rm":
                case ".rmvb":
                    CustomRenderRMGraph(_fileName);
                    break;
                default:
                    break;
            }
        }

        private void CustomRenderWMVGraph(string _fileName) {

            Type type = Type.GetTypeFromCLSID(new Guid("6B6D0800-9ADA-11D0-A520-00A0D10129C0"));
            object obj = Activator.CreateInstance(type);
            ((IFileSourceFilter)obj).Load(_fileName, null);
            IBaseFilter source = obj as IBaseFilter;

            filterGraph.AddFilter(source, "WMV Reader");

            IEnumPins pins;
            IPin[] pin = new IPin[1];
            source.EnumPins(out pins);
            IntPtr fetched = IntPtr.Zero;

            while (pins.Next(1, pin, fetched) == 0) {
                filterGraph.Render(pin[0]);
                Marshal.ReleaseComObject(pin[0]);
            }
            Marshal.ReleaseComObject(pins);
            Marshal.ReleaseComObject(source);
            Marshal.ReleaseComObject(obj);
        }

        private void CustomRenderRMGraph(string _fileName) {
            Type type = Type.GetTypeFromCLSID(new Guid("765035B3-5944-4A94-806B-20EE3415F26F"));
            object obj = Activator.CreateInstance(type);
            ((IFileSourceFilter)obj).Load(_fileName, null);
            IBaseFilter source = obj as IBaseFilter;
            filterGraph.AddFilter(source, "Real Reader");

            IEnumPins pins;
            IPin[] pin = new IPin[1];
            source.EnumPins(out pins);
            IntPtr fetched = IntPtr.Zero;

            while (pins.Next(1, pin, fetched) == 0) {
                filterGraph.Render(pin[0]);
                Marshal.ReleaseComObject(pin[0]);
            }
            Marshal.ReleaseComObject(pins);
            Marshal.ReleaseComObject(source);
            Marshal.ReleaseComObject(obj);
        }

        private void RemoveAllFilters() {
            int hr = 0;

            mediaControl.Stop();
            IEnumFilters enumFilters;
            ArrayList filtersArray = new ArrayList();

            hr = filterGraph.EnumFilters(out enumFilters);
            DsError.ThrowExceptionForHR(hr);

            IBaseFilter[] filters = new IBaseFilter[1];
            IntPtr fetched = IntPtr.Zero;

            try
            {
                while (enumFilters.Next(filters.Length, filters, fetched) == 0)
                {
                    filtersArray.Add(filters[0]);
                }
            }
            catch { }

            foreach (IBaseFilter filter in filtersArray) {
                hr = filterGraph.RemoveFilter(filter);
                while (Marshal.ReleaseComObject(filter) > 0) ;
            }
        }

        public bool RemoveVideo() {
            int hr = 0;
            IBaseFilter ibfRenderer = null;
            hr = filterGraph.FindFilterByName("Video Renderer", out ibfRenderer);
            if (hr > 0) return false;
            hr = filterGraph.RemoveFilter(ibfRenderer);
            if (hr > 0) return false;
            if (ibfRenderer != null) {
                Marshal.ReleaseComObject(ibfRenderer);
                ibfRenderer = null;
            }
            return true;

        }

        public bool RemoveAudio() {
            int hr = 0;
            IBaseFilter ibfRenderer = null;
            hr = filterGraph.FindFilterByName("Default DirectSound Device", out ibfRenderer);
            if (hr > 0) return false;
            hr = filterGraph.RemoveFilter(ibfRenderer);
            if (hr > 0) return false;
            if (ibfRenderer != null) {
                Marshal.ReleaseComObject(ibfRenderer);
                ibfRenderer = null;
            }
            return true;
        }

        private void InitFilterGraph() {
            RemoveAllFilters();

            int hr;

            IBaseFilter ibfRenderer = null;
            ISampleGrabber sampGrabber = null;
            IBaseFilter capFilter = null;
            IPin iPinInFilter = null;
            IPin iPinOutFilter = null;
            IPin iPinInDest = null;

            try {
                //// Get the default video renderer
                //ibfRenderer = (IBaseFilter)new VideoRendererDefault();

                //// Add it to the graph
                //hr = filterGraph.AddFilter(ibfRenderer, "Ds.NET VideoRendererDefault");
                //DsError.ThrowExceptionForHR(hr);
                //iPinInDest = DsFindPin.ByDirection(ibfRenderer, PinDirection.Input, 0);

                switch (System.IO.Path.GetExtension(_fileName).ToLower())
                {
                    case ".mpeg":
                    case ".vob":
                    case ".dat":
                    case ".mpg":
                        #region 优先使用MainConcept MPEG Video Decoder
                        //try
                        //{
                        //    Type type = Type.GetTypeFromCLSID(new Guid("2BE4D140-6F2E-4B3A-B0BD-E880917238DC"));
                        //    object obj = Activator.CreateInstance(type);

                        //    IBaseFilter tmpFilter = obj as IBaseFilter;
                        //    if (tmpFilter != null)
                        //        filterGraph.AddFilter(tmpFilter, "MainConcept MPEG Video Decoder");
                        //}
                        //catch { }
                        #endregion
                        break;
                }

                hr = filterGraph.RenderFile(_fileName, null);
                if (hr != 0) {
                    if (!_saftyMode) {
                        RemoveAllFilters();
                        CustomRenderFileGraph(_fileName);
                    } else {
                        DsError.ThrowExceptionForHR(hr);
                    }
                }
                hr = filterGraph.FindFilterByName("Default DirectSound Device", out ibfRenderer);
                _hasAudio = hr == 0;
                hr = filterGraph.FindFilterByName("Video Renderer", out ibfRenderer);
                _hasVideo = hr == 0;

                if (_textEnabled && _hasVideo) {
                    iPinInDest = DsFindPin.ByDirection(ibfRenderer, PinDirection.Input, 0);

                    iPinInDest.ConnectedTo(out iPinOutFilter);
                    iPinInDest.Disconnect();
                    iPinOutFilter.Disconnect();


                    // Get the SampleGrabber interface
                    sampGrabber = new SampleGrabber() as ISampleGrabber;

                    IBaseFilter baseGrabFlt = sampGrabber as IBaseFilter;
                    ConfigureSampleGrabber(sampGrabber);


                    // Add the frame grabber to the graph
                    hr = filterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber");
                    DsError.ThrowExceptionForHR(hr);

                    iPinInFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);

                    // Connect the graph.  Many other filters automatically get added here
                    hr = filterGraph.Connect(iPinOutFilter, iPinInFilter);
                    DsError.ThrowExceptionForHR(hr);

                    Marshal.ReleaseComObject(iPinOutFilter);
                    iPinOutFilter = null;

                    iPinOutFilter = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);

                    hr = filterGraph.Connect(iPinOutFilter, iPinInDest);
                    DsError.ThrowExceptionForHR(hr);

                    SaveSizeInfo(sampGrabber);
                }
            } finally {
                if (capFilter != null) {
                    Marshal.ReleaseComObject(capFilter);
                    capFilter = null;
                }
                if (sampGrabber != null) {
                    Marshal.ReleaseComObject(sampGrabber);
                    sampGrabber = null;
                }
                if (ibfRenderer != null) {
                    Marshal.ReleaseComObject(ibfRenderer);
                    ibfRenderer = null;
                }
                if (iPinInFilter != null) {
                    Marshal.ReleaseComObject(iPinInFilter);
                    iPinInFilter = null;
                }
                if (iPinOutFilter != null) {
                    Marshal.ReleaseComObject(iPinOutFilter);
                    iPinOutFilter = null;
                }
                if (iPinInDest != null) {
                    Marshal.ReleaseComObject(iPinInDest);
                    iPinInDest = null;
                }
            }

            if (_hasVideo) SetupBitmap();
        }

        public void Resize()
        {
            try
            {
                if (videoWindow != null && (this.CurrentState != MediaPlayerState.NoFile && this.CurrentState != MediaPlayerState.OpeningFile))
                {
                    hr = videoWindow.SetWindowPosition(0, 0, ownerRegion.Right, ownerRegion.Bottom);

                    SetupScaleMode();
                }
            }
            catch { };
        }


        private void InitVideoWindow() {
            if (videoWindow == null) return;
            if (_showVideoWindow && _hasVideo) {
                if (owner != IntPtr.Zero) {
                    hr = videoWindow.put_Owner(owner);
                    //DsError.ThrowExceptionForHR(hr);

                    hr = videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings);
                    //DsError.ThrowExceptionForHR(hr);

                    hr = videoWindow.put_Visible(OABool.True);
                    //DsError.ThrowExceptionForHR(hr);

                    hr = videoWindow.SetWindowPosition(0, 0, ownerRegion.Right, ownerRegion.Bottom);
                    //DsError.ThrowExceptionForHR(hr);

                    if (parentOwner != IntPtr.Zero)
                        videoWindow.put_MessageDrain(parentOwner);
                } else {
                    hr = videoWindow.put_Owner(IntPtr.Zero);
                    //DsError.ThrowExceptionForHR(hr);

                    hr = videoWindow.put_Visible(OABool.True);
                    //DsError.ThrowExceptionForHR(hr);
                }
            } else {
                videoWindow.put_AutoShow(OABool.False);
                hr = videoWindow.put_Owner(IntPtr.Zero);
                hr = videoWindow.put_Visible(OABool.False);
            }
        }

        //设置图像显示比例和尺寸
        private void SetupScaleMode() {
            if (basicVideo != null && _hasVideo) {
                int destWidth = 0,
                    destHeight = 0,
                    destTop = 0,
                    destLeft = 0,
                    videoWidth = 0,
                    videoHeight = 0,
                    videoWindowWidth = 0,
                    videoWindowHeight = 0;

                double videoScale = 1.0f,
                    windowScale = 1.0f;

                videoWindow.get_Width(out videoWindowWidth);
                videoWindow.get_Height(out videoWindowHeight);

                basicVideo.get_DestinationWidth(out destWidth);
                basicVideo.get_DestinationHeight(out destHeight);
                windowScale = (double)videoWindowWidth / videoWindowHeight;
                basicVideo.get_VideoWidth(out videoWidth);
                basicVideo.get_VideoHeight(out videoHeight);
                videoScale = (double)videoWidth / videoHeight;

                if (videoWidth > 0 && videoHeight > 0) {
                    switch (_scaleMode) {
                        case MediaPlayerVideoScaleMode.Stretch:
                            destWidth = videoWindowWidth;
                            destHeight = videoWindowHeight;
                            break;
                        case MediaPlayerVideoScaleMode.Original:
                            destWidth = videoWidth;
                            destHeight = videoHeight;
                            break;
                        case MediaPlayerVideoScaleMode.HalfScale:
                            destWidth = videoWidth / 2;
                            destHeight = videoHeight / 2;
                            break;
                        case MediaPlayerVideoScaleMode.DoubleScale:
                            destWidth = videoWidth * 2;
                            destHeight = videoHeight * 2;
                            break;
                        case MediaPlayerVideoScaleMode.Crop:
                            if (windowScale < videoScale) {
                                destWidth = (int)(videoWindowHeight * videoScale);
                                destHeight = videoWindowHeight;
                            } else {
                                destWidth = videoWindowWidth;
                                destHeight = (int)(videoWindowWidth / videoScale);
                            }

                            break;
                        case MediaPlayerVideoScaleMode.MaintainAspect:
                            if (windowScale > videoScale) {
                                destWidth = (int)(videoWindowHeight * videoScale);
                                destHeight = videoWindowHeight;
                            } else {
                                destWidth = videoWindowWidth;
                                destHeight = (int)(videoWindowWidth / videoScale);
                            }
                            break;
                    }

                    destLeft = (videoWindowWidth - destWidth) / 2;
                    destTop = (videoWindowHeight - destHeight) / 2;

                    basicVideo.put_DestinationWidth(destWidth);
                    basicVideo.put_DestinationHeight(destHeight);
                    basicVideo.put_DestinationLeft(destLeft);
                    basicVideo.put_DestinationTop(destTop);
                }
            }
        }

        //初始化视频信息
        private void InitMediaInfo() {
            if (_hasVideo) {
                basicVideo = filterGraph as IBasicVideo;
                if (basicVideo != null) {
                    SetupScaleMode();
                }
            } else {
                basicVideo = null;
            }

            if (mediaSeeking != null)
            {
                //获得视频检索能力
                AMSeekingSeekingCapabilities cap;
                mediaSeeking.GetCapabilities(out cap);

                //获得视频播放时间
                if (cap.CompareTo(AMSeekingSeekingCapabilities.CanGetDuration) >= 0)
                {
                    mediaSeeking.GetDuration(out _videoDuration);
                }
                else
                {
                    _videoDuration = -1;
                }

                //获得视频是否可检索
                if (cap.CompareTo(AMSeekingSeekingCapabilities.CanSeekAbsolute | AMSeekingSeekingCapabilities.CanSeekBackwards | AMSeekingSeekingCapabilities.CanSeekForwards) >= 0)
                {

                    mediaSeeking.SetPositions(DsLong.FromInt64(0), AMSeekingSeekingFlags.AbsolutePositioning, DsLong.FromInt64(_videoDuration), AMSeekingSeekingFlags.AbsolutePositioning);
                    _canSeek = true;
                }
                else
                {
                    _canSeek = false;
                }
            }

            _canStep = GetFrameStepInterface();

        }

        //
        // Some video renderers support stepping media frame by frame with the
        // IVideoFrameStep interface.  See the interface documentation for more
        // details on frame stepping.
        //
        private bool GetFrameStepInterface() {
            int hr = 0;

            IVideoFrameStep frameStepTest = null;

            // Get the frame step interface, if supported
            frameStepTest = (IVideoFrameStep)this.filterGraph;

            if (frameStepTest == null) return false;

            // Check if this decoder can step
            hr = frameStepTest.CanStep(0, null);
            if (hr == 0) {
                frameStep = frameStepTest;
                return true;
            } else {
                //Marshal.ReleaseComObject(frameStepTest);
                return false;
            }
        }

        /// <summary> Read and store the properties </summary>
        private void SaveSizeInfo(ISampleGrabber sampGrabber) {
            int hr;

            // Get the media type from the SampleGrabber
            AMMediaType media = new AMMediaType();
            hr = sampGrabber.GetConnectedMediaType(media);
            DsError.ThrowExceptionForHR(hr);

            if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero)) {
                throw new NotSupportedException("Unknown Grabber Media Format");
            }

            // Grab the size info
            VideoInfoHeader videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader));
            _videoWidth = videoInfoHeader.BmiHeader.Width;
            _videoHeight = videoInfoHeader.BmiHeader.Height;
            _videoStride = _videoWidth * (videoInfoHeader.BmiHeader.BitCount / 8);

            DsUtils.FreeAMMediaType(media);
            media = null;
        }

        /// <summary> Set the options on the sample grabber </summary>
        private void ConfigureSampleGrabber(ISampleGrabber sampGrabber) {
            AMMediaType media;
            int hr;

            // Set the media type to Video/RBG24
            media = new AMMediaType();
            media.majorType = MediaType.Video;
            media.subType = MediaSubType.RGB32;
            media.formatType = FormatType.VideoInfo;

            hr = sampGrabber.SetMediaType(media);
            DsError.ThrowExceptionForHR(hr);

            DsUtils.FreeAMMediaType(media);
            media = null;

            hr = sampGrabber.SetCallback(this, 1);
            DsError.ThrowExceptionForHR(hr);
        }

        //获得文字部分的尺寸
        private SizeF MeasureString(string text, Font font, StringFormat stringFormat) {
            Graphics g = Graphics.FromImage(new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppRgb));
            return g.MeasureString(text, font, new PointF(0, 0), stringFormat);
        }

        //初始化覆盖层
        void SetupBitmap() {
            //如果视频未初始化，退出
            if (_videoWidth == 0) return;

            int fSize;

            // scale the font size in some portion to the video image
            fSize = 3 * (_videoWidth / 128);
            if (fSize < 12) fSize = 12;

            if (String.IsNullOrEmpty(_textFontName)) {
                _textFont = new Font(FontFamily.GenericSerif, fSize);
            } else {
                _textFont = new Font(_textFontName, fSize);
            }

            fSize /= 2;

            switch (_textDirection) {
                case MediaPlayerTextScrollDirection.DownAndRightToLeft:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = _videoWidth;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
                case MediaPlayerTextScrollDirection.DownAndLeftToRight:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = -_textSize.Width;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed);
                    break;
                case MediaPlayerTextScrollDirection.UpAndRightToLeft:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = _videoHeight - _textAreaBitmap.Height;
                    _textPosition.X = _videoWidth;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
                case MediaPlayerTextScrollDirection.UpAndLeftToRight:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = _videoHeight - _textAreaBitmap.Height;
                    _textPosition.X = -_textSize.Width;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed);
                    break;
                case MediaPlayerTextScrollDirection.LeftAndUpToDown:
                    _textStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap((int)(_textSize.Width + fSize), _videoHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = (int)((float)_textAreaBitmap.Width - _textSize.Width) / 2;
                    _textPosition.Y = _videoHeight;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed);
                    break;
                case MediaPlayerTextScrollDirection.LeftAndDownToUp:
                    _textStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap((int)(_textSize.Width + fSize), _videoHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = (int)((float)_textAreaBitmap.Width - _textSize.Width) / 2;
                    _textPosition.Y = 0;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
                case MediaPlayerTextScrollDirection.RightAndUpToDown:
                    _textStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap((int)(_textSize.Width + fSize), _videoHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = _videoWidth - _textAreaBitmap.Width;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = (int)((float)_textAreaBitmap.Width - _textSize.Width) / 2;
                    _textPosition.Y = _videoHeight;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed);
                    break;
                case MediaPlayerTextScrollDirection.RightAndDownToUp:
                    _textStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap((int)(_textSize.Width + fSize), _videoHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = _videoWidth - _textAreaBitmap.Width;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = (int)((float)_textAreaBitmap.Width - _textSize.Width) / 2;
                    _textPosition.Y = 0;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
                case MediaPlayerTextScrollDirection.CenterAndRightToLeft:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = (_videoHeight - _textAreaBitmap.Height) / 2;
                    _textPosition.X = _videoWidth;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
                case MediaPlayerTextScrollDirection.CenterAndLeftToRight:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = (_videoHeight - _textAreaBitmap.Height) / 2;
                    _textPosition.X = -_textSize.Width;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed);
                    break;
                default:
                    _textStringFormat.FormatFlags = 0;
                    _textSize = MeasureString(_textString, _textFont, _textStringFormat);
                    _textAreaBitmap = new Bitmap(_videoWidth, (int)(_textSize.Height + fSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    _textAreaPosition.X = 0;
                    _textAreaPosition.Y = 0;
                    _textPosition.X = _videoWidth;
                    _textPosition.Y = (int)((float)_textAreaBitmap.Height - _textSize.Height) / 2;
                    _textScrollSpeed = Math.Abs(_textScrollSpeed) * -1;
                    break;
            }

            _textAreaGraph = Graphics.FromImage(_textAreaBitmap);
            _textAreaGraph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        }
        #endregion

        public IntPtr Owner {
            set {
                owner = value;
            }
        }

        public IntPtr ParentOwner
        {
            set { parentOwner = value; }
        }

        public Rectangle OwnerRegion
        {
            get { return ownerRegion; }
            set { ownerRegion = value; Resize(); }
        }

        public string CurrentFileName {
            set {
                _fileName = value;
            }
            get {
                return _fileName;
            }
        }

        public long Duration {
            get {
                return _videoDuration;
            }
        }

        public long CurrentPos {
            get {
                long pos = 0;
                AMSeekingSeekingCapabilities cap;
                mediaSeeking.GetCapabilities(out cap);

                if (cap.CompareTo(AMSeekingSeekingCapabilities.CanGetCurrentPos) >= 0) {
                    mediaSeeking.GetCurrentPosition(out pos);
                }
                return pos;
            }
            set {
                mediaSeeking.SetPositions(DsLong.FromInt64(value), AMSeekingSeekingFlags.AbsolutePositioning, DsLong.FromInt64(_videoDuration), AMSeekingSeekingFlags.AbsolutePositioning);
            }
        }

        public bool CanSeek {
            get {
                return _canSeek;
            }
        }


        public void ToggleFullScreen() {
            int hr = 0;
            OABool lMode;

            // Don't bother with full-screen for audio-only files
            if ((!_hasVideo) || (this.videoWindow == null))
                return;

            // Read current state
            hr = this.videoWindow.get_FullScreenMode(out lMode);
            DsError.ThrowExceptionForHR(hr);

            if (lMode == OABool.False) {
                //// Save current message drain
                //hr = this.videoWindow.get_MessageDrain(out hDrain);
                //DsError.ThrowExceptionForHR(hr);

                //// Set message drain to application main window
                //hr = this.videoWindow.put_MessageDrain(owner.Parent.Handle);
                //DsError.ThrowExceptionForHR(hr);

                // Switch to full-screen mode
                lMode = OABool.True;
                hr = this.videoWindow.put_FullScreenMode(lMode);
                DsError.ThrowExceptionForHR(hr);
                this._isFullScreen = true;
            } else {
                // Switch back to windowed mode
                lMode = OABool.False;
                hr = this.videoWindow.put_FullScreenMode(lMode);
                DsError.ThrowExceptionForHR(hr);

                //// Undo change of message drain
                //hr = this.videoWindow.put_MessageDrain(hDrain);
                //DsError.ThrowExceptionForHR(hr);

                // Reset video window
                hr = this.videoWindow.SetWindowForeground(OABool.True);
                DsError.ThrowExceptionForHR(hr);

                // Reclaim keyboard focus for player application
                //this.Focus();
                this._isFullScreen = false;
            }
        }

        public static Image Capture(string fileName, int second) {
            using (MediaPlayer mp = new MediaPlayer()) {
                mp.TextEnabled = true;
                mp.TextString = String.Empty;
                mp.ShowVideoWindow = false;
                mp.Open(fileName, false, false);
                //mp.RemoveVideo();
                mp.RemoveAudio();

                if (mp.CurrentState == MediaPlayerState.Ready) {
                    return mp.Capture(second);
                }
                return null;
            }
        }

        public Image Capture(int second) {
            if (_currentState == MediaPlayerState.Ready
                || _currentState == MediaPlayerState.Playing
                || _currentState == MediaPlayerState.Paused
                || _currentState == MediaPlayerState.Stopped
                || _currentState == MediaPlayerState.Buffering
            ) {
                Play();
                Seek(second);
                manualResetEventForCaptureImage.Reset();
                if (manualResetEventForCaptureImage.WaitOne(5000, true)) {
                    Pause();
                    Image returnImage = (Image)currentVideoImage.Clone();
                    returnImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
                    return returnImage;
                }
            }
            Pause();
            return null;
        }

        public Image Capture()
        {
            int bufferSize = 0;
            int hr = basicVideo.GetCurrentImage(ref bufferSize, IntPtr.Zero);
            IntPtr p = Marshal.AllocHGlobal(bufferSize);
            BitmapInfoHeader header;
            Image img = null;
            try
            {
                hr = basicVideo.GetCurrentImage(ref bufferSize, p);
                DsError.GetErrorText(hr);

                header = (BitmapInfoHeader)Marshal.PtrToStructure(p, typeof(BitmapInfoHeader));

                byte[] buffer = new byte[bufferSize];
                Marshal.Copy(p, buffer, 0, bufferSize);

                IntPtr pBuffer = Marshal.AllocHGlobal(header.ImageSize);
                Marshal.Copy(buffer, header.Size, pBuffer, header.ImageSize);
                
                switch (header.BitCount)
                {
                    case 16:
                        img = new Bitmap(header.Width, header.Height, header.BitCount * header.Width / 8, System.Drawing.Imaging.PixelFormat.Format16bppRgb555, p);
                        break;
                    case 24:
                        img = new Bitmap(header.Width, header.Height, header.BitCount * header.Width / 8, System.Drawing.Imaging.PixelFormat.Format24bppRgb, p);
                        break;
                    case 32:
                        img = new Bitmap(header.Width, header.Height, header.BitCount * header.Width / 8, System.Drawing.Imaging.PixelFormat.Format32bppRgb, pBuffer);
                        break;
                    default:
                        img = new Bitmap(header.Width, header.Height, header.BitCount * header.Width / 8, System.Drawing.Imaging.PixelFormat.Undefined, p);
                        break;
                }
                if (img != null) img.RotateFlip(RotateFlipType.RotateNoneFlipY);
            }
            finally
            {
                Marshal.FreeHGlobal(p);
            }
            return img;
        }


        public void Play() {
            if (_currentState != MediaPlayerState.Playing) {
                if (_currentState == MediaPlayerState.Ready ||
                    _currentState == MediaPlayerState.Stopped ||
                    _currentState == MediaPlayerState.Paused) {
                    SetupScaleMode();
                    if (mediaControl != null)
                    {
                        mediaControl.Run();
                        ChangeCurrentState(MediaPlayerState.Playing);
                    }
                    
                } else {
                    ProcessOnError("播放器当前不可播放！", new Exception("播放器当前不可播放！"));
                }
            }
        }

        public void Step() {
            Pause();
            if (_canStep) {
                hr = this.frameStep.Step(1, null);
            }
        }

        public void Stop() {
            if (_currentState != MediaPlayerState.Stopped) {
                mediaControl.Stop();
                ChangeCurrentState(MediaPlayerState.Stopped);
            }
        }

        public void Pause() {
            if (_currentState != MediaPlayerState.Paused) {
                mediaControl.Pause();
                ChangeCurrentState(MediaPlayerState.Paused);
            }
        }

        public void Forward() {
        }

        public void Forward(int seconds) {
        }

        public void Backward() {
        }

        public void Backward(int seconds) {
        }

        public void Seek(decimal percent)
        {
            if (percent >= 0.0M && percent <= 1.0M)
            {
                mediaSeeking.SetPositions(DsLong.FromInt64((long)(_videoDuration * percent)), AMSeekingSeekingFlags.AbsolutePositioning, DsLong.FromInt64(_videoDuration), AMSeekingSeekingFlags.AbsolutePositioning);
            }
        }

        public void Seek(int second) {
            mediaSeeking.SetPositions(DsLong.FromInt64(second * 10000000), AMSeekingSeekingFlags.AbsolutePositioning, DsLong.FromInt64(_videoDuration), AMSeekingSeekingFlags.AbsolutePositioning);
        }

        public void Seek(TimeSpan time) {
            mediaSeeking.SetPositions(DsLong.FromInt64(time.Ticks), AMSeekingSeekingFlags.AbsolutePositioning, DsLong.FromInt64(_videoDuration), AMSeekingSeekingFlags.AbsolutePositioning);
        }

        public void Mute() {
        }

        //清理回收资源
        private void CloseInterfaces() {
            int hr = 0;

            try {
                Close();
            } catch { }
            try {
                lock (this) {
                    // Relinquish ownership (IMPORTANT!) after hiding video window

                    if (threadEvent != null)
                    {
                        if (threadEvent.IsAlive)
                            threadEvent.Abort();
                        threadEvent = null;
                    }

                    try
                    {
                        if (this.videoWindow != null)
                        {
                            hr = this.videoWindow.put_Visible(OABool.False);
                            //DsError.ThrowExceptionForHR(hr);
                            hr = this.videoWindow.put_Owner(IntPtr.Zero);
                            //DsError.ThrowExceptionForHR(hr);
                        }
                    }
                    catch { }

                    // Release and zero DirectShow interfaces
                    if (this.mediaEventEx != null) {
                        try
                        {
                            hr = this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                            //DsError.ThrowExceptionForHR(hr);
                        }
                        catch { }
                    }

                    if (this.mediaSeeking != null) {
                        try
                        {
                            Marshal.ReleaseComObject(mediaSeeking);
                        }
                        catch { }
                        this.mediaSeeking = null;
                    }

                    if (this.mediaPosition != null)
                    {
                        try
                        {
                            Marshal.ReleaseComObject(mediaPosition);
                        }
                        catch { }
                        this.mediaPosition = null;
                    }

                    if (this.mediaControl != null)
                    {
                        try
                        {
                            Marshal.ReleaseComObject(mediaControl);
                        }
                        catch { }
                        this.mediaControl = null;
                    }

                    if (this.basicAudio != null) {
                        try
                        {
                            Marshal.ReleaseComObject(basicAudio);
                        }
                        catch { }
                        this.basicAudio = null;
                    }
                    if (this.basicVideo != null) {
                        try
                        {
                            Marshal.ReleaseComObject(basicVideo);
                        }
                        catch { }
                        this.basicVideo = null;
                    }
                    if (this.videoWindow != null) {
                        try
                        {
                            Marshal.ReleaseComObject(videoWindow);
                        }
                        catch { }
                        this.videoWindow = null;
                    }
                    if (this.frameStep != null) {
                        try
                        {
                            Marshal.ReleaseComObject(frameStep);
                        }
                        catch { }
                        this.frameStep = null;
                    }

                    if (this.mediaEventEx != null)
                    {
                        try
                        {
                            Marshal.ReleaseComObject(mediaEventEx);
                        }
                        catch { }
                        this.mediaEventEx = null;
                    }

                    if (this.filterGraph != null) {
                        try
                        {
                            Marshal.ReleaseComObject(this.filterGraph);
                        }
                        catch { }
                        this.filterGraph = null;
                    }

                    GC.Collect();
                }
            } catch {
            }
        }

        #region IDisposable 成员

        public void Dispose() {
            CloseInterfaces();
        }

        #endregion

        #region ISampleGrabberCB 成员

        public int SampleCB(double SampleTime, IMediaSample pSample) {
            throw new Exception("The method or operation is not implemented.");
        }

        public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) {
            //Graphics g = Graphics.FromImage(bitmapOverlay);
            //g.Clear(Color.Transparent);
            //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            //TimeSpan ts = new TimeSpan(CurrentPos);
            //string timeString = ts.Minutes.ToString("00") + ':' + ts.Seconds.ToString("00") + '/';
            //ts = new TimeSpan(_videoDuration);
            //timeString += ts.Minutes.ToString("00") + ':' + ts.Seconds.ToString("00");
            //SizeF size = g.MeasureString(timeString, timeFont);

            //g.DrawString(timeString, timeFont, System.Drawing.Brushes.Red, 0, 0);

            //bitmapOverlay.RotateFlip(RotateFlipType.RotateNoneFlipY);

            //Bitmap temp = new Bitmap(_videoWidth, _videoHeight, _videoStride, System.Drawing.Imaging.PixelFormat.Format32bppArgb, pBuffer);
            //g = Graphics.FromImage(temp);
            //g.DrawImage(bitmapOverlay, 0, 0);

            //g.Dispose();
            //temp.Dispose();

            if (_textEnabled) {
                _textAreaGraph.Clear(Color.FromArgb(60, _textBGColor));

                _textAreaGraph.DrawString(_showString, _textFont, Brushes.White, _textPosition, _textStringFormat);
                switch (_textDirection) {
                    case MediaPlayerTextScrollDirection.DownAndRightToLeft:
                    case MediaPlayerTextScrollDirection.UpAndRightToLeft:
                    case MediaPlayerTextScrollDirection.CenterAndRightToLeft:
                        _textPosition.X += _textScrollSpeed;
                        if (_textPosition.X + _textSize.Width < 0) _textPosition.X = _videoWidth;
                        break;
                    case MediaPlayerTextScrollDirection.DownAndLeftToRight:
                    case MediaPlayerTextScrollDirection.UpAndLeftToRight:
                    case MediaPlayerTextScrollDirection.CenterAndLeftToRight:
                        _textPosition.X += _textScrollSpeed;
                        if (_textPosition.X > _videoWidth) _textPosition.X = -_textSize.Width;
                        break;
                    case MediaPlayerTextScrollDirection.LeftAndDownToUp:
                    case MediaPlayerTextScrollDirection.RightAndDownToUp:
                        _textPosition.Y += _textScrollSpeed;
                        if (_textPosition.Y + _textSize.Height < 0) _textPosition.Y = _videoHeight;
                        break;
                    case MediaPlayerTextScrollDirection.LeftAndUpToDown:
                    case MediaPlayerTextScrollDirection.RightAndUpToDown:
                        _textPosition.Y += _textScrollSpeed;
                        if (_textPosition.Y > _videoHeight) _textPosition.Y = -_textSize.Height;
                        break;
                }

                _textAreaBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
                currentVideoImage = new Bitmap(_videoWidth, _videoHeight, _videoStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
                manualResetEventForCaptureImage.Set();

                Graphics g = Graphics.FromImage(currentVideoImage);
                g.DrawImage(_textAreaBitmap, _textAreaPosition);

                g.Dispose();
            }
            return 0;
        }

        #endregion
    }
}
